﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Core;
using Core.Base;
using Global.Mgr;
using UnityEngine;
using Object = UnityEngine.Object;

public enum EAudioSuffix
{
    mp3,
    wav
}

/// <summary>
/// 音频管理器
/// </summary>
public class AudioMgr : BaseMgr
{
    private AudioSource bgSource;
    private GameObject audioObj;

    /// <summary>
    /// 空闲的AudioSource
    /// </summary>
    private List<AudioSource> idlePool = new List<AudioSource>();

    /// <summary>
    /// 工作中的AudioSource
    /// </summary>
    private List<AudioSource> workPool = new List<AudioSource>();

    /// <summary>
    /// 音效源最大数量
    /// </summary>
    private const int EffectSouceMaxCount = 10;

    private List<Coroutine> audioEffectCoroutines = new List<Coroutine>();
    private List<Action> audioEffectCallback = new List<Action>();
    private float effectVolume = 1;

    /// <summary>
    /// 背景音乐音量
    /// </summary>
    public float BGMVolume
    {
        get { return bgSource.volume; }
        set
        {
            float result = Mathf.Clamp01(value);
            bgSource.volume = result;
        }
    }

    /// <summary>
    /// 全局的音效音量
    /// </summary>
    public float EffectVolume
    {
        get => effectVolume;
        set
        {
            float result = Mathf.Clamp01(value);
            effectVolume = result;
            workPool.ForEach(delegate(AudioSource source) { source.volume = effectVolume; });
            idlePool.ForEach(delegate(AudioSource source) { source.volume = effectVolume; });
        }
    }

    public override void OnInit()
    {
        audioObj = new GameObject("AudioMgr");
        bgSource = audioObj.AddComponent<AudioSource>();
        bgSource.volume = 0.7f;
        bgSource.playOnAwake = false;
        for (int i = 0; i < EffectSouceMaxCount; i++)
        {
            AudioSource effectSource = audioObj.AddComponent<AudioSource>();
            effectSource.playOnAwake = false;
            effectSource.volume = effectVolume;
            effectSource.loop = false;
            idlePool.Add(effectSource);
        }

        Object.DontDestroyOnLoad(audioObj);
    }


    public void PlayBgAudio(string fullName, EAudioSuffix suffix = EAudioSuffix.mp3, bool isLoop = true)
    {
        KKGL.ResMgr.LoadAsync<AudioClip>($"Audio/{fullName}", (re) =>
        {
            if (re != null)
            {
                PlayBgAudio(re, isLoop);
            }
        });
    }

    public void PlayBgAudio(AudioClip audioClip, bool isLoop = true)
    {
        bgSource.clip = audioClip;
        bgSource.Play();
        bgSource.loop = isLoop;
    }

    public void PauseBgAudio()
    {
        bgSource.Pause();
    }

    public void ResumeBgAudio()
    {
        if (bgSource.clip != null)
        {
            bgSource.Play();
        }
    }

    public void StopBgAudio()
    {
        if (bgSource != null && bgSource.clip)
        {
            bgSource.Stop();
            bgSource.clip = null;
        }
    }


    /// <summary>
    /// 播放音效
    /// </summary>
    /// <param name="fullName"></param>
    /// <param name="volumeScale">音量</param>
    /// <param name="suffix">相对于全局音量的缩放，0~1</param>
    /// <param name="clipLengthCb">加载到AudioClip后回调返回该音效的长度</param>
    /// <param name="endCallback">播放结束时的回调</param>
    public void PlayEffectAudio(string fullName, float volumeScale = 1, EAudioSuffix suffix = EAudioSuffix.mp3,
        Action<float> clipLengthCb = null, Action endCallback = null)
    {
        KKGL.ResMgr.LoadAsync<AudioClip>($"Audio/{fullName}", (re) =>
        {
            if (re != null)
            {
                PlayEffectAudio(re, volumeScale, clipLengthCb, endCallback);
            }
        });
    }


    /// <summary>
    /// 播放音效
    /// </summary>
    /// <param name="fullName"></param>
    /// <param name="volumeScale">相对于全局音量的缩放 ,0~1</param>
    /// <param name="clipLengthCb">加载到AudioClip后回调返回该音效的长度</param>
    /// <param name="endCallback">播放结束时的回调</param>
    public void PlayEffectAudio(AudioClip audioClip, float volumeScale = 1, Action<float> clipLengthCb = null,
        Action endCallback = null)
    {
        if (idlePool.Count == 0)
        {
            Debug.LogWarning("播放失败,EffectSource无空闲");
        }
        else
        {
            volumeScale = Mathf.Clamp01(volumeScale);
            clipLengthCb?.Invoke(audioClip.length);
            AudioSource audioSource = idlePool[0];
            audioSource.clip = audioClip;
            audioSource.volume = volumeScale * EffectVolume;
            audioSource.Play();
            workPool.Add(audioSource);
            idlePool.Remove(audioSource);
            float musicLength = audioClip.length;
            Coroutine coroutine = KKGL.MainBehaviour.StartCoroutine(
                DelayCallback(endCallback, musicLength, audioSource));

            audioEffectCoroutines.Add(coroutine);
            audioEffectCallback.Add(endCallback);
        }
    }

    /// <summary>
    /// 停止所有音效
    /// </summary>
    public void StopAllEffect()
    {
        for (int i = 0; i < audioEffectCoroutines.Count; i++)
        {
            KKGL.MainBehaviour.StopCoroutine(audioEffectCoroutines[i]);
        }

        audioEffectCoroutines.Clear();

        audioEffectCallback.Clear();

        for (int i = 0; i < workPool.Count; i++)
        {
            workPool[0].Stop();
            idlePool.Add(workPool[0]);
            workPool.RemoveAt(0);
        }
    }

    public void StopAllAudio()
    {
        if (bgSource != null)
        {
            bgSource.clip = null;
        }

        var idleEnumerator = idlePool.GetEnumerator();
        while (idleEnumerator.MoveNext())
        {
            if (idleEnumerator.Current != null)
            {
                idleEnumerator.Current.clip = null;
            }
        }

        var workEnumerator = workPool.GetEnumerator();
        while (workEnumerator.MoveNext())
        {
            if (workEnumerator.Current != null)
            {
                workEnumerator.Current.clip = null;
            }
        }
    }


    IEnumerator DelayCallback(Action callback, float delay, AudioSource audioSource)
    {
        yield return new WaitForSeconds(delay);
        if (audioSource != null)
        {
            workPool.Remove(audioSource);
            idlePool.Add(audioSource);
        }

        callback?.Invoke();
    }
}